/*************************************************************
 * TwoSocks for network connection toy.
 *
 * This software utilize two sockets in modes of server and
 * client. Actually two sockets handle two connections.
 * 
 * (C) Copyright by Hojin Choi <hojin.choi@gmail.com>
 *
 * License: Apache License v2.0
 * Last Date: 2010.07.11
 * Version: 0.1
 *************************************************************/

#ifndef TWOSOCKS_H
#define TWOSOCKS_H

#ifdef HAVE_STDINT_H
#include <stdint.h>
#else
#include <limits.h>
typedef unsigned short uint16_t;
#if LONG_BIT == 32
typedef unsigned long uint32_t;
#elif UINT_MAX == 0xffffffff
typedef unsigned int uint32_t;
#else
#error "Can't define 32 bit unsigned intger type"
#endif
#endif

#if HAVE_NETINET_IN_H
#include <netinet/in.h>
#else
#error "Can't define internet address container."
#endif

#if HAVE_NETINET_IN_H
#include <netdb.h>
#else
#error "Can't define hostent struct."
#endif


#ifdef HAVE_SYS_QUEUE_H
#include <sys/queue.h>
#endif

#include "event.h"

#define TS_BUFMAX          2048
#define AF_PRIVATE_STDIO   (AF_MAX+1)

enum ts_state
{
	NONE_NONE,
	ACCEPTING_NONE,
	ACCEPTING_ACCEPTING,
	CONNECTING_CONNECTING,
	RECEIVING_RECEIVING,
	RECEIVING_CONNECTING,
	CONNECTING_RECEIVING,
	RECEIVING_ACCEPTING,
	ACCEPTING_RECEIVING,
	CLOSED_RECEIVING,
	RECEIVING_CLOSED,
	END_OF_STATE
};

enum ts_engine 
{
	TS_NONE,
	TS_SERVER_SERVER,
	TS_SERVER_CLIENT,
	TS_CLIENT_SERVER,
	TS_CLIENT_CLIENT,
	END_OF_ENGINE
};

enum ts_socket_index
{
	TS_FIRST = 0,
	TS_SECOND = 1
};

struct ts_address
{
	int    family;
	struct sockaddr bindaddr;
	int    len_bindaddr;

	struct addrinfo * addr;
	int    port;

	int    inherited_fd;
};

struct options
{
	int engine;
	struct ts_address nodes[2];
	const char * args[2];
};

struct global_options
{
	int verbosity;
};

struct _evt_sock
{
	struct event evt;
	struct bufferevent * bevt;
	int fd;
};

struct twosockctx;

typedef TAILQ_HEAD(twosock_item, twosockctx) * list_head_t;
typedef TAILQ_ENTRY(twosockctx) list_item_t;
#define list_init TAILQ_INIT
#define list_foreach(v,h) TAILQ_FOREACH(v,h,list_item)
#define list_insert_head(h, v) TAILQ_INSERT_HEAD(h,v,list_item)
#define list_remove(h,v) TAILQ_REMOVE(h,v,list_item)

struct twosockctx
{
	list_item_t list_item; /* list_item(fixed) ize..*/

	enum ts_engine engine;
	enum ts_state state;

	struct _evt_sock sock[2];

	const struct options * opts;
	list_head_t ctxlist_head;
};

void init_options(  struct options * );
void server_server( const struct options * );
void server_client( const struct options * );
void client_server( const struct options * );
void client_client( const struct options * );

int ts_create_server( struct twosockctx * ctx, int index );
int ts_create_client( struct twosockctx * ctx, int index );

struct twosockctx * ts_alloc( int engine, int state, const struct options * );
void ts_close( struct twosockctx * ctx );
void ts_free( struct twosockctx * );

void ts_accept ( int fd, short event, void * _twosockctx );
void ts_connect( int fd, short event, void * _twosockctx );

void ts_read( struct bufferevent * bevt, void * _twosockctx );
void ts_write( struct bufferevent * bevt, void * _twosockctx );
void ts_error( struct bufferevent * bevt, short what, void * _twosockctx );

void ts_log( const char * fmt, ... );
void ts_exit( int status, const char * fmt, ... );

typedef void (*engine_t)( const struct options * );
#endif /* TWOSOCKS_H */
